﻿using System.Collections.Generic;
using System.Linq;
using FitNesseRunner;
using FitNesseRunner.Model;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FitNesseRunnerTests
{
	/// <summary>
	/// Summary description for ExecutionListProviderShould
	/// </summary>
	[TestClass]
	public class ExecutionListProviderShould
	{
		private ExecutionListProvider<FakeFolder> _provider;
		private FakeFolder _rootFolder;
		
		[TestInitialize]
		public void Setup()
		{
			_provider = new ExecutionListProvider<FakeFolder>(new FakeFolder.Manager());
			_rootFolder = new FakeFolder(ExecutionListProvider.RootFolderName, FolderType.None);
		}

		[TestMethod]
		public void HandleEmptySuite()
		{
			var folder = _rootFolder.CreateSubFolder(FolderType.Suite);
			var executionList = GetExecutionListForSuite(folder);

			CollectionAssertEx.AreEqual(new FakeFolder[0], executionList);
		}

		[TestMethod]
		public void ProvideSingleTest()
		{
			var folder = _rootFolder.CreateSubFolder(FolderType.Test);
			var executionList = GetExecutionListForTest(folder);
			CollectionAssertEx.AreEqual(new[] {folder}, executionList);
		}

		[TestMethod]
		public void ProvideSingleTestFromSuite()
		{
			var suiteFolder = _rootFolder.CreateSubFolder(FolderType.Suite);
			var testFolder = suiteFolder.CreateSubFolder(FolderType.Test);
			var executionList = GetExecutionListForSuite(suiteFolder);
			CollectionAssertEx.AreEqual(new[] { testFolder }, executionList);
		}

		[TestMethod]
		public void ProvideAllTestsInASuite()
		{
			var suiteFolder = _rootFolder.CreateSubFolder("Suite", FolderType.Suite);
			var testFolder1 = suiteFolder.CreateSubFolder("Test1", FolderType.Test);
			var testFolder2 = suiteFolder.CreateSubFolder("Test2", FolderType.Test);
			
			var executionList = GetExecutionListForSuite(suiteFolder);

			CollectionAssertEx.AreEquivallent(new[] {testFolder1, testFolder2}, executionList);
		}

		[TestMethod]
		public void ReturnSuiteSetUpBeforeTest()
		{
			var suite = _rootFolder.CreateSubFolder(FolderType.Suite);
			var suiteSetup = suite.CreateSubFolder(ExecutionListProvider.SuiteSetUpFolderName);
			var test = suite.CreateSubFolder(FolderType.Test);
			
			var results = GetExecutionListForTest(test);

			CollectionAssertEx.AreEqual(new[] { suiteSetup, test }, results);
		}

		[TestMethod]
		public void ReturnSuiteSetUpBeforeSuite()
		{
			var suite = _rootFolder.CreateSubFolder(FolderType.Suite);
			var test1 = suite.CreateSubFolder("A", FolderType.Test);
			var suiteSetup = suite.CreateSubFolder(ExecutionListProvider.SuiteSetUpFolderName);
			var test2 = suite.CreateSubFolder("Z", FolderType.Test);
			
			var results = GetExecutionListForSuite(suite);

			Assert.IsTrue(results.First().Equals(suiteSetup));
			CollectionAssertEx.AreEquivallent(new[] {test1, test2}, results.Skip(1));
		}

		[TestMethod]
		public void ReturnSuiteTearDownAfterTest()
		{
			var suite = _rootFolder.CreateSubFolder(FolderType.Suite);
			var suiteTearDown = suite.CreateSubFolder(ExecutionListProvider.SuiteTearDownFolderName);
			var test = suite.CreateSubFolder(FolderType.Test);
			
			var results = GetExecutionListForTest(test);

			CollectionAssertEx.AreEqual(new[] { test, suiteTearDown }, results);
		}

		[TestMethod]
		public void ReturnSuiteTearDownAfterSuite()
		{
			var suite = _rootFolder.CreateSubFolder(FolderType.Suite);
			var test1 = suite.CreateSubFolder("A", FolderType.Test);
			var suiteTearDown = suite.CreateSubFolder(ExecutionListProvider.SuiteTearDownFolderName);
			var test2 = suite.CreateSubFolder("Z", FolderType.Test);
			
			var results = GetExecutionListForSuite(suite);

			Assert.IsTrue(results.Last().Equals(suiteTearDown));
			CollectionAssertEx.AreEquivallent(new[] { test1, test2 }, results.Take(2));
		}

		[TestMethod]
		public void ReturnSuiteSetUpFromAncestorFolder()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
				var innerSuite = outerSuite.CreateSubFolder("Inner", FolderType.Suite);
					var test = innerSuite.CreateSubFolder("Test", FolderType.Test);
				var suiteSetup = outerSuite.CreateSubFolder(ExecutionListProvider.SuiteSetUpFolderName);

			var results = GetExecutionListForTest(test);

			CollectionAssertEx.AreEqual(new[] {suiteSetup, test}, results);
		}

		[TestMethod]
		public void ReturnSuiteTearDownFromAncestorFolder()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
			var innerSuite = outerSuite.CreateSubFolder("Inner", FolderType.Suite);
			var test = innerSuite.CreateSubFolder("Test", FolderType.Test);
			var suiteTearDown = outerSuite.CreateSubFolder(ExecutionListProvider.SuiteTearDownFolderName);

			var results = GetExecutionListForTest(test);

			CollectionAssertEx.AreEqual(new[] { test, suiteTearDown }, results);
		}

		[TestMethod]
		public void ReturnTestsFromInnerSuites()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
				var innerSuite = outerSuite.CreateSubFolder("InnerSuite", FolderType.Suite);
					var test = innerSuite.CreateSubFolder("InnerTest", FolderType.Test);

			var results = GetExecutionListForSuite(outerSuite);

			CollectionAssertEx.AreEqual(new[] {test}, results);
		}

		[TestMethod]
		public void ReturnTestsRecursively()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
				var outerTest = outerSuite.CreateSubFolder("OuterTest", FolderType.Test);
				var innerSuite = outerSuite.CreateSubFolder("Inner", FolderType.Suite);
					var innerTest = innerSuite.CreateSubFolder("InnerTest", FolderType.Test);

			var results = GetExecutionListForSuite(outerSuite);

			CollectionAssertEx.AreEqual(new[] { innerTest, outerTest }, results);
		}

		[TestMethod]
		public void MultipleSetups()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
				var outerSetup = outerSuite.CreateSubFolder(ExecutionListProvider.SuiteSetUpFolderName);
				var innerSuite = outerSuite.CreateSubFolder("Inner", FolderType.Suite);
					var innerSetup = innerSuite.CreateSubFolder(ExecutionListProvider.SuiteSetUpFolderName);
					var innerTest = innerSuite.CreateSubFolder("InnerTest", FolderType.Test);
				var outerTest = outerSuite.CreateSubFolder("OuterTest", FolderType.Test);

			var results = GetExecutionListForSuite(outerSuite);

			CollectionAssertEx.AreEqual(new[] { innerSetup, innerTest, outerSetup, outerTest }, results);
		}

		[TestMethod]
		public void ProvideRootFolder()
		{
			var outerSuite = _rootFolder.CreateSubFolder("Outer", FolderType.Suite);
			var innerSuite = outerSuite.CreateSubFolder("Inner", FolderType.Suite);
			innerSuite.CreateSubFolder("Test", FolderType.Test);

			var root = _provider.GetRoot(innerSuite);

			Assert.AreEqual(_rootFolder, root);
		}

		[TestMethod]
		public void SetSetupFolderOnTestFolders()
		{
			var suite = _rootFolder.CreateSubFolder("Suite");
			var setup = suite.CreateSubFolder(ExecutionListProvider.SetUpFolderName);
			var test = suite.CreateSubFolder("Test", FolderType.Test);

			var results = _provider.Test(test);

			Assert.AreEqual(1, results.Count());
			Assert.AreEqual(test, results.Single().Folder);
			Assert.AreEqual(setup, results.Single().SetUpFolder);
		}

		[TestMethod]
		public void SetTearDownFolderOnTestFolders()
		{
			var suite = _rootFolder.CreateSubFolder("Suite");
			var tearDown = suite.CreateSubFolder(ExecutionListProvider.TearDownFolderName);
			var test = suite.CreateSubFolder("Test", FolderType.Test);

			var results = _provider.Test(test);

			Assert.AreEqual(1, results.Count());
			Assert.AreEqual(test, results.Single().Folder);
			Assert.AreEqual(tearDown, results.Single().TearDownFolder);
		}

		private IEnumerable<FakeFolder> GetExecutionListForSuite(FakeFolder folder)
		{
			return from testFolder in _provider.Suite(folder)
			       select testFolder.Folder;
		}

		private IEnumerable<FakeFolder> GetExecutionListForTest(FakeFolder folder)
		{
			return from testFolder in _provider.Test(folder)
			       select testFolder.Folder;
		}
	}

	public class FakeFolder : IFolder
	{
		private readonly List<FakeFolder> _children = new List<FakeFolder>();
		public string Name { get; private set; }

		public FolderType FolderType { get; private set; }

		public FakeFolder(string folderName, FolderType folderType)
		{
			Name = folderName;
			FolderType = folderType;
		}

		private FakeFolder _parent;

		public FakeFolder CreateSubFolder(string subFolderName)
		{
			return CreateSubFolder(subFolderName, FolderType.None);
		}

		public override string ToString()
		{
			return (_parent != null
			        	? _parent + "."
			        	: string.Empty) + Name;
		}

		public FakeFolder CreateSubFolder(FolderType folderType)
		{
			return CreateSubFolder(null, folderType);
		}

		public FakeFolder CreateSubFolder(string subFolderName, FolderType folderType)
		{
			var subFolder = new FakeFolder(subFolderName, folderType);
			_children.Add(subFolder);
			subFolder._parent = this;
			return subFolder;
		}

		public class Manager : IFolderManager<FakeFolder>
		{
			public FakeFolder GetParent(FakeFolder folder)
			{
				return folder._parent;
			}

			public IEnumerable<FakeFolder> GetChildren(FakeFolder folder)
			{
				return folder._children;
			}
		}
	}
}
